﻿using Core.Extension;
using Core.Model;
using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Core.SqlExtension
{
    public static class ResolveExpression
    {
        private const char OpenQuote = '[';
        private const char CloseQuote = ']';
        private const char ParameterPrefix = '@';

        //
        public static ProviderOption ProviderOption = new ProviderOption(OpenQuote, CloseQuote, ParameterPrefix);


        /// <summary>
        /// selector
        /// </summary>
        /// <param name="propertyInfos"></param>
        /// <param name="selector"></param>
        /// <param name="PageSize"></param>
        /// <returns></returns>
        public static string ResolveSelect(PropertyInfo[] propertyInfos, LambdaExpression selector)
        {
            var selectSql = "";

            if (selector == null)
            {
                var propertyBuilder = new StringBuilder();
                foreach (var propertyInfo in propertyInfos)
                {
                    if (propertyBuilder.Length > 0)
                        propertyBuilder.Append(",");
                    propertyBuilder.AppendFormat($"{ProviderOption.CombineFieldName(propertyInfo.GetColumnAttributeName()) } AS {  ProviderOption.CombineFieldName(propertyInfo.Name)}");
                }
                selectSql = propertyBuilder.ToString();
            }
            else
            {
                var nodeType = selector.Body.NodeType;
                if (nodeType == ExpressionType.MemberAccess)
                {
                    var columnName = ((MemberExpression)selector.Body).Member.GetColumnAttributeName();
                    selectSql = ProviderOption.CombineFieldName(columnName);
                }
                else if (nodeType == ExpressionType.MemberInit)
                {
                    var memberInitExpression = (MemberInitExpression)selector.Body;
                    selectSql = string.Join(",", memberInitExpression.Bindings.Select(a => ProviderOption.CombineFieldName(a.Member.GetColumnAttributeName())));
                }
                else if (nodeType == ExpressionType.New)
                {
                    var newExpression = ((NewExpression)selector.Body);
                    selectSql = string.Join(",", newExpression.Members.Select(a => ProviderOption.CombineFieldName(a.GetColumnAttributeName())));
                }
            }
            return selectSql;
        }

        /// <summary>
        /// search table
        /// </summary>
        /// <param name="TableType"></param>
        /// <param name="isNeedFrom"></param>
        /// <returns></returns>
        public static string FormatTableName(Type TableType, bool isNeedFrom = false)
        {
            var typeOfTableClass = TableType;

            var tableName = typeOfTableClass.GetTableAttributeName();

            var SqlString = $" {ProviderOption.CombineFieldName(tableName)} ";
            if (isNeedFrom)
                SqlString = " FROM " + SqlString;

            return SqlString;
        }

        /// <summary>
        /// where condition
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public static WhereExpression ResolveWhere(LambdaExpression whereExpression, string prefix = null)
        {
            var where = new WhereExpression(whereExpression, prefix, ProviderOption);

            return where;
        }


        /// <summary>
        /// where condition object T
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static UpdateEntityWhereExpression ResolveWhere(object obj)
        {
            var where = new UpdateEntityWhereExpression(obj, ProviderOption);
            where.Resolve();
            return where;
        }

        /// <summary>
        /// order 
        /// </summary>
        /// <param name="orderbyExpressionDic"></param>
        /// <returns></returns>
        public static string ResolveOrderBy(Dictionary<EOrderBy, LambdaExpression> orderbyExpressionDic)
        {
            if (orderbyExpressionDic == null || !orderbyExpressionDic.Any())
                return "";

            var orderByList = orderbyExpressionDic.Select(a =>
            {
                var columnName = ((MemberExpression)a.Value.Body).Member.GetColumnAttributeName();
                return ProviderOption.CombineFieldName(columnName) + (a.Key == EOrderBy.Asc ? " ASC " : " DESC ");
            }).ToList();

            if (!orderByList.Any())
                return "";

            return "ORDER BY " + string.Join(",", orderByList);
        }

        /// <summary>
        /// Top
        /// </summary>
        /// <param name="PageSize"></param>
        /// <returns></returns>
        public static string ResolveTop(int PageSize)
        {
            string sqlTop = "";
            if (PageSize > 0)
            {
                sqlTop = $" TOP {PageSize} ";
            }
            return sqlTop;
        }


        /// <summary>
        /// Insert Entity Attribute && VALUES
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>

        public static EntityAttribute FormatInsertParamsAndValues<T>(T entity)
        {
            var paramSqlBuilder = new StringBuilder(64);
            var valueSqlBuilder = new StringBuilder(64);
            Dictionary<string, object> dicParamsValue = new Dictionary<string, object>();

            var properties = entity.GetProperties();
            var isAppend = false;
            foreach (var propertiy in properties)
            {
                if (isAppend)
                {
                    paramSqlBuilder.Append(",");
                    valueSqlBuilder.Append(",");
                }

                var columnName = propertiy.GetColumnAttributeName();
                var paramterName = ProviderOption.ParameterPrefix + columnName;
                paramSqlBuilder.Append(ProviderOption.CombineFieldName(columnName));
                valueSqlBuilder.Append(paramterName);

                isAppend = true;

                //获取值
                var Value = propertiy.GetValue(entity);
                dicParamsValue.Add(paramterName, Value);
            }

            //查询表名
            var fromTableSql = ResolveExpression.FormatTableName(typeof(T));
            //组成sql 语句
            var SqlString = $"INSERT INTO {fromTableSql} ({paramSqlBuilder.ToString()}) VALUES({valueSqlBuilder.ToString()})";

            return new EntityAttribute() { attributeParams = paramSqlBuilder.ToString(), attributeParamsValues = valueSqlBuilder.ToString(), attributeValues = dicParamsValue, attributeSqlStr = SqlString };
        }

        /// <summary>
        ///  UPDATE Entity Attribute && VALUES
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="updateExpression"></param>
        /// <returns></returns>
        public static EntityAttribute ResolveUpdateParamsAndVaules<T>(T model)
        {
            //查询表名
            var fromTableSql = ResolveExpression.FormatTableName(typeof(T));


            //更新实体
            Expression<Func<T, T>> updateExpression = a => model;
            var update = new UpdateExpression(updateExpression, ProviderOption);
            
            //主键更新
            var where = ResolveWhere(model);
            var whereSql = where.SqlCmd;

            //组成sql 语句
            var SqlString = $"UPDATE {fromTableSql} {update.SqlCmd} {whereSql}";

            #region 真实值
            Dictionary<string, object> paramsValues = new Dictionary<string, object>();

            //列值
            foreach (var item in update.Param)
            {
                paramsValues.Add(item.Key, item.Value);
            }

            //条件值
            foreach (var item in where.Param)
            {
                paramsValues.Add(item.Key, item.Value);
            }
            #endregion

            return new EntityAttribute() { attributeParamsValues = update.SqlCmd.ToString(), attributeValues = paramsValues, attributeSqlStr = SqlString };
        }

        /// <summary>
        ///  UPDATE Entity Attribute && VALUES &&SELECTOR
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="updateExpression"></param>
        /// <returns></returns>
        public static EntityAttribute ResolveUpdateSelectorParamsAndVaules<T>(Expression<Func<T, T>> selector)
        {
            //查询表名
            var fromTableSql = ResolveExpression.FormatTableName(typeof(T));

            //更新实体
            // Expression<Func<T, T>> updateExpression = a => model;
           var update = new UpdateExpression(selector, ProviderOption);


            ////主键更新
            var IdObject =Guid.Empty;
            var memberInitExpression = ((MemberInitExpression)selector.Body);
            foreach (var item in memberInitExpression.Bindings)
            {
                var memberAssignment = (MemberAssignment)item;
                
                var paramName = memberAssignment.Member.Name;
                if (paramName == "Id")
                {
                    switch (memberAssignment.Expression.NodeType)
                    {
                        case ExpressionType.Constant:
                            var constantExpression = (ConstantExpression)memberAssignment.Expression;
                            IdObject=Guid.Parse(constantExpression.Value.ToString());
                            break;
                        case ExpressionType.MemberAccess:
                            var constantValue = ((MemberExpression)memberAssignment.Expression).MemberToValue();
                            IdObject = Guid.Parse(constantValue.ToString());
                            break; ;
                    }
                    break;
                }
            }
             var whereSql =" WHERE Id=@Id ";

            ////组成sql 语句
            var SqlString = $"UPDATE {fromTableSql} {update.SqlCmd} {whereSql}";

            #region 真实值
            Dictionary<string, object> paramsValues = new Dictionary<string, object>();

            //列值
            foreach (var item in update.Param)
            {
                paramsValues.Add(item.Key, item.Value);
            }

            //条件值
            paramsValues.Add("@Id", IdObject);
            #endregion

            return new EntityAttribute() { attributeParamsValues = update.SqlCmd.ToString(), attributeValues = paramsValues, attributeSqlStr = SqlString };   
        }


        /// <summary>
        ///  Delete Entity Attribute && VALUES
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        public static EntityAttribute ResolveDeleteParamsAndVaules<T>(T model)
        {
            //查询表名
            var fromTableSql = ResolveExpression.FormatTableName(typeof(T));
            //组成sql 语句
            var SqlString = $"UPDATE {fromTableSql} SET ISDELETE='1' WHERE [ID]=@Id";

            //获取主键value
            var KeyPropertity=ReflectExtension.GetKeyPropertity(model);
            var val=KeyPropertity.GetValue(model);

            //主键值
            Dictionary<string, object> dicParamsAndValues = new Dictionary<string, object>();
            dicParamsAndValues.Add("@ID", val);

            return new EntityAttribute() { attributeValues= dicParamsAndValues,  attributeSqlStr = SqlString };
        }

        /// <summary>
        ///  Romove Entity Attribute && VALUES
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        public static EntityAttribute ResolveRomoveParamsAndVaules<T>(T model)
        {
            //查询表名
            var fromTableSql = ResolveExpression.FormatTableName(typeof(T));
            //组成sql 语句
            var SqlString = $"delete {fromTableSql}  WHERE [ID]=@Id";

            //获取主键value
            var KeyPropertity = ReflectExtension.GetKeyPropertity(model);
            var val = KeyPropertity.GetValue(model);

            //主键值
            Dictionary<string, object> dicParamsAndValues = new Dictionary<string, object>();
            dicParamsAndValues.Add("@ID", val);
            
            return new EntityAttribute() { attributeValues = dicParamsAndValues, attributeSqlStr = SqlString };
        }
    }
}
